{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DkA0Fobtb9dM"
   },
   "source": [
    "##### Copyright 2022 The Cirq Developers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "tUshu7YfcAAW"
   },
   "outputs": [],
   "source": [
    "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "RIbn0pYo2E-Q"
   },
   "source": [
    "# Qubit Picking"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4j6Gq31OtDDk"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://quantumai.google/cirq/hardware/qubit_picking\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/main/docs/hardware/qubit_picking.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/main/docs/hardware/qubit_picking.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/hardware/qubit_picking.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "f53d26973380"
   },
   "source": [
    "When running a circuit on a noisy quantum hardware device, the choice and even ordering of hardware qubits used directly affects how reliably the device measures a correct result. This notebook covers some of the available qubit error information that can be useful for picking good hardware qubits to run your circuit on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "pBl-1eHNHXS8"
   },
   "outputs": [],
   "source": [
    "# @title Setup\n",
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    print(\"installed cirq.\")\n",
    "    import cirq\n",
    "\n",
    "import cirq_google\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Q6YMeWnvDaMu"
   },
   "source": [
    "## State of qubit selection in Cirq\n",
    "\n",
    "Cirq assumes that circuits you intend to run on quantum hardware (virtual or otherwise) are able to be placed on the device, and assumes that you are able to perform placement by hand. The information in this notebook serves to help you identify which qubits are the best to use, but it is up to you to map the qubits in your circuit to the \"good\" qubits available on the device.\n",
    "\n",
    "## Error characterization data\n",
    "\n",
    "Cirq provides characterization error data that is intended to represent median performance of actual Google quantum hardware as accurately as possible. The primary use of this data is for creating a `cirq.NoiseModel` for use in a [Quantum Virtual Machine](../simulate/quantum_virtual_machine.ipynb), but it also provides information on what qubits are the best to use for your circuit. \n",
    "\n",
    "The following code demonstrates how to load that noise data as a `cirq_google.GoogleNoiseProperties` object, which specifies the available data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "hKfu5yVi2Ebs"
   },
   "outputs": [],
   "source": [
    "processor_id = \"rainbow\"\n",
    "noise_props = cirq_google.engine.load_device_noise_properties(processor_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YS1j_WzgpumG"
   },
   "source": [
    "### One qubit gate Pauli error data\n",
    "\n",
    "Pauli error defines decoherence of a single qubit in one of the Pauli [channels](../noise/representing_noise.ipynb) X, Y, or Z. If the errors are distributed in the uniform distribution over all three axes, the probability of applying an erroneous Pauli gate X, Y, or Z will be the Pauli error divided by three. See page 11 of [this Supplementary Information document](https://arxiv.org/abs/1910.11333){:.external} for more on Pauli error. \n",
    "\n",
    "Below is the single qubit Pauli error for the `cirq.PhasedXZGate` supported by the Rainbow processor, pulled from the `gate_pauli_errors` attribute of the noise properties object. You can inspect the error for the other supported one-qubit gates by replacing the `gate` variable below. However, as of July 19th, 2022, the error estimation process results in identical Pauli error for all one-qubit gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "E70fyCU7puGr"
   },
   "outputs": [],
   "source": [
    "print(f\"One qubit error data: gate_pauli_errors\")\n",
    "print(f\"Supported Gates: {noise_props.single_qubit_gates()}\")\n",
    "fig, ax = plt.subplots(figsize=(10, 10))\n",
    "gate = cirq.PhasedXZGate\n",
    "measures = {\n",
    "    op_id.qubits: pauli_error\n",
    "    for op_id, pauli_error in noise_props.gate_pauli_errors.items()\n",
    "    if op_id.gate_type == gate\n",
    "}\n",
    "ax.set_title(f\"{gate.__name__} Pauli error\")\n",
    "_ = cirq.Heatmap(measures).plot(ax)\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YZVP6TI8uIeL"
   },
   "source": [
    "The provided error data shows identical Pauli error for all gate types, with relatively higher error for qubits `(4,1)` and `(7,6)`. Qubit `(7,2)` is noticeably lower error than the others, and could be prioritized to be mapped to a circuit qubit that executes many single-qubit operations. However, this is only one type of error; you should inspect the other error types before committing to use any particular qubits."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_J58phZlVm9L"
   },
   "source": [
    "### Two qubit gate Pauli error data\n",
    "\n",
    "The two qubit gates also cause Pauli decoherence, which is also stored in the `gate_pauli_errors` attribute. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Q4SNArjfMkB4"
   },
   "outputs": [],
   "source": [
    "two_qubit_gates = noise_props.two_qubit_gates()\n",
    "print(f\"Two qubit error data: gate_pauli_errors\")\n",
    "fig, axes = plt.subplots(1, 2, figsize=(20, 10))\n",
    "axes = iter(axes)\n",
    "for gate in two_qubit_gates:\n",
    "    measures = {\n",
    "        op_id.qubits: pauli_error\n",
    "        for op_id, pauli_error in noise_props.gate_pauli_errors.items()\n",
    "        if op_id.gate_type == gate\n",
    "    }\n",
    "    if measures:\n",
    "        ax = next(axes)\n",
    "        ax.set_title(f\"{gate.__name__} Pauli error\")\n",
    "        _ = cirq.TwoQubitInteractionHeatmap(measures).plot(ax)\n",
    "\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "pQtxZBXYt1P5"
   },
   "source": [
    "This Pauli data informs that the qubit pairs `(6,2)-(7,2)` and `(7,2)-(7,3)` should be avoided at all costs, but the other qubits are roughly comparable."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "s8DEXJdKW38f"
   },
   "source": [
    "### Two qubit gate FSim error\n",
    "\n",
    "FSim error represents [coherent](../noise/representing_noise.ipynb#channels) two-qubit error that cannot be represented by Pauli channels (that is, it results from entanglement). Instead, it is represented as an additional `cirq.PhasedFSimGate` with small parameters that would be applied to a given qubit pair after any two-qubit gate's execution.\n",
    "\n",
    "The following example takes the norm of a couple of the parameters, as a rough approximate representation of the goodness of qubit pairs relative to one another. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "e1MXeJaKW3j0"
   },
   "outputs": [],
   "source": [
    "print(f\"Two qubit error data: fsim_errors\")\n",
    "two_qubit_gates = noise_props.two_qubit_gates()\n",
    "fig, axes = plt.subplots(1, 2, figsize=(20, 10))\n",
    "axes = iter(axes)\n",
    "for gate in two_qubit_gates:\n",
    "    measures = {\n",
    "        op_id.qubits: fsim_refit_gate\n",
    "        for op_id, fsim_refit_gate in noise_props.fsim_errors.items()\n",
    "        if op_id.gate_type == gate\n",
    "    }\n",
    "    if measures:\n",
    "        ax = next(axes)\n",
    "        # Norm the Fsim refit gate parameters as an approximate of how good a qubit is.\n",
    "        measures = {\n",
    "            qubits: np.linalg.norm([fsim_refit_gate.theta, fsim_refit_gate.phi])\n",
    "            for qubits, fsim_refit_gate in measures.items()\n",
    "        }\n",
    "        ax.set_title(f\"{gate.__name__} Pauli error\")\n",
    "        _ = cirq.TwoQubitInteractionHeatmap(measures).plot(ax)\n",
    "\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0SKqbogVtSFs"
   },
   "source": [
    "This fsim data would influence you to avoid the `(6,2)-(7,2)` qubit pair, and prefer the top left of the grid for high-priority qubit pairs  (those that have two-qubit gates executed on them many times in your circuit). Additionally, the fact that the `cirq.SycamoreGate` error is so much lower than the `cirq.ISwapPowGate` error means the device was likely calibrated for the `SycamoreGate`, and you should [transform](../transform/transformers.ipynb) your circuit with `cirq.optimize_for_target_gateset` to the `cirq_google.transformers.SycamoreTargetGateset` gate set, if possible.\n",
    "\n",
    "This particular type of error can be compensated for somewhat by using [Floquet calibration](https://www.youtube.com/watch?v=hYDWOz1r2Ys&t=243s), but this is out of scope for this notebook, and may require adaptation to work with the provided processor error data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BfJharjIarJb"
   },
   "source": [
    "### Readout error\n",
    "\n",
    "Readout error manifests as a measurement of $|0\\rangle$ that should have been $|1\\rangle$, or vice versa. Note that this is different from the Pauli error induced by the `cirq.MeasurementGate`. It's important to note that, while the magnitude of measurement error is higher in general than that of Pauli error, it should be not automatically be considered to be more impactful. This is due to the fact that Pauli error can affect qubits other than the measured one and that effective readout error compensation strategies exist (that won't be discussed here)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "nSDwjnLebmVb"
   },
   "outputs": [],
   "source": [
    "print(f\"One qubit error data: readout_errors\")\n",
    "fig, axes = plt.subplots(1, 2, figsize=(20, 10))\n",
    "for i, ax, title in zip(\n",
    "    range(2), axes.flat, [\"True |0> measured as |1>\", \"True |1> measured as |0>\"]\n",
    "):\n",
    "    measures = {\n",
    "        qubit: readout_error[i] for qubit, readout_error in noise_props.readout_errors.items()\n",
    "    }\n",
    "    ax.set_title(title)\n",
    "    _ = cirq.Heatmap(measures).plot(ax, vmax=0.4, vmin=0)\n",
    "fig.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CipARD58sq_d"
   },
   "source": [
    "This readout data indicates that you definitely want to avoid qubit `(7,2)`. If you're really trying to eke out the best performance possible, you may want to avoid qubits `(5,2)` and `(5,3)` as well. Note how the \"True $|1\\rangle$ measured as $|0\\rangle$\" error representing common decay is far larger and more impactful than the random excitation of $|0\\rangle$ to $|1\\rangle$ error. This is typical behavior and you can safely prioritize working around common decay error instead of random excitation error in most cases."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WLfGbpVIune1"
   },
   "source": [
    "## Overall qubit picking strategy\n",
    "\n",
    "Even though the single Pauli qubit data preferred qubit `(7,2)`, the coupled pairs attached to that qubit have abysmal error rates, meaning it should be avoided at all costs. Qubit `(4,1)` has some interesting properties in that its two-qubit gates are quite good, and so is its readout, but not its single-qubit Pauli error. It may be useful to use this qubit in an entangled system where only the other qubits have single-qubit gates applied to them. All of the other gates are roughly comparable based on these error rates. Ultimately, the choice of which hardware qubits have which circuit operations applied to them is up to your discretion."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JwGtMdoxc-fu"
   },
   "source": [
    "# Summary\n",
    "\n",
    "Cirq provides the following error types which serve to represent an actual Google quantum hardware device as accurately as currently possible: \n",
    "- One- and two-qubit Pauli error for each `cirq.Gate` supported by the device\n",
    "- Two-qubit Fsim error for each supported two-qubit gate that models non-Pauli error. \n",
    "- Readout error for each qubit that models error in measurements.\n",
    "\n",
    "While often used to create [QVM](../simulate/quantum_virtual_machine.ipynb)s, this data is also useful in the process of finding good qubits to use. By avoiding high error qubits whenever possible, you can maximize the reliability of your circuit. \n",
    "\n",
    "If you're working with an actual device that may experience drift, or a change in behavior over time, you may need to use more advanced noise compensation strategies, which you can read about in the [Noise](../noise) category."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "qubit_picking.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
